package core

import "fmt"

type Parser struct {
	tokens []Token
	//current      int
	peek         int
	currentToken Token
	peekToken    Token
}

// NewParser creates a new parser
func NewParser(tokens []Token) *Parser {
	if len(tokens) == 0 {
		tokens = []Token{{Type: EOF}, {Type: EOF}}
	} else if len(tokens) == 1 {
		tokens = append(tokens, Token{Type: EOF})
	}
	p := &Parser{
		tokens:       tokens,
		currentToken: tokens[0],
		peekToken:    tokens[1],
		peek:         1,
	}
	return p
}

// nextToken advances the current token
func (p *Parser) nextToken() {
	p.currentToken = p.peekToken
	if p.peek >= len(p.tokens)-1 {
		return
	}
	p.peek++
	p.peekToken = p.tokens[p.peek]
}

func (p *Parser) ParseProgram() ([]Expression, error) {
	exprs := []Expression{}
	for {
		expr, err := p.ParseExpression()
		if expr == nil && err == nil {
			break
		}
		if err != nil {
			return nil, err
		}
		exprs = append(exprs, expr)
		p.nextToken()
	}
	return exprs, nil
}

func (p *Parser) ParseExpression() (Expression, error) {
	switch p.currentToken.Type {
	case LPAREN:
		return p.parseList()
	case SYMBOL:
		return p.parseSymbol()
	case BOOLEAN:
		return p.parseBoolean()
	case EOF:
		return nil, nil
	default:
		return nil, fmt.Errorf("unexpected token: %s", p.currentToken.Lexeme)
	}
}

func (p *Parser) parseList() (Expression, error) {
	// Advance past the opening parenthesis
	p.nextToken()

	// Create a list to hold the elements
	list := &ListNode{nodes: []Node{}}

	// Parse elements until we reach the closing parenthesis
	for p.currentToken.Type != RPAREN {
		if p.currentToken.Type == EOF {
			return nil, fmt.Errorf("unexpected end of input, expected ')'")
		}

		// Parse the current expression
		expr, err := p.ParseExpression()
		if err != nil {
			return nil, err
		}

		// Add the expression to our list
		list.nodes = append(list.nodes, expr)

		// Advance to the next token
		p.nextToken()
	}

	return list, nil
}

func (p *Parser) parseSymbol() (Expression, error) {
	s := &SymbolNode{token: p.currentToken}
	return s, nil
}

func (p *Parser) parseBoolean() (Expression, error) {
	b := &BooleanNode{token: p.currentToken}
	return b, nil
}
